package org.durandj.ystream.server.message;

import java.util.Collections;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.logging.Logger;

/**
 * Handles messages that are given to it. Messages are queued up and the
 * handler must unqueue them and handle each individually. Each message
 * is sorted into the queue so that messages that have a higher priority
 * are found farther up in the queue.
 * 
 * @author durandj
 * @version 1.0
 *
 */
public abstract class MessageHandler
{
	// TODO: Blocking messages may be needed...
	
	/**
	 * Stores messages similar to a queue but also does priority sorting
	 * to allow for messages that need to be handled first.
	 * 
	 * @author durandj
	 * @version 1.0
	 *
	 */
	private class MessageQueue
	{
		/**
		 * Stores the messages.
		 */
		private LinkedList<Message> queue;
		
		/**
		 * Creates an empty message queue.
		 */
		public MessageQueue()
		{
			queue = new LinkedList<Message>();
		}
		
		/**
		 * Queues a message up.
		 * 
		 * @param msg - the message
		 */
		public synchronized void queue(Message msg)
		{
			if(msg == null)
				throw new NullPointerException("Must give a valid message.");
			
			queue.add(msg);
			Collections.sort(queue);
		}
		
		/**
		 * Dequeues a message from the queue.
		 * 
		 * @return the first item in the queue
		 */
		public synchronized Message dequeue()
		{
			if(queue.isEmpty())
				throw new NoSuchElementException("No more elements in the queue.");
			
			return queue.remove();
		}
		
		/**
		 * Checks if the queue is empty.
		 * 
		 * @return is it empty?
		 */
		public synchronized boolean isEmpty()
		{
			return queue.isEmpty();
		}
	}
	
	/**
	 * Logs messages.
	 */
	protected Logger log = Logger.getLogger(this.getClass().getName());
	// TODO: This will need to be redirected to a log file instead of the console.
	
	/**
	 * The message queue.
	 */
	private MessageQueue messages;
	
	/**
	 * Creates the message handler basics.
	 */
	public MessageHandler()
	{
		messages = new MessageQueue();
	}
	
	/**
	 * Queues a message up for the message handler to take care of.
	 * 
	 * @param msg - the message to queue
	 */
	public void queueMessage(Message msg)
	{
		log.info("Message received...\n" +
		         "\tPriority: " + msg.getPriority() + "\n" +
				 "\tID: " + msg.getID() + "\n" +
				 "\tSender: " + msg.getSender().getClass().getSimpleName() + "\n");
		
		synchronized(messages) {
			messages.queue(msg);
			messages.notifyAll();
		}
	}
	
	/**
	 * Gets the next message from the message queue.
	 * 
	 * @return the next message
	 */
	protected Message nextMessage()
	{
		return messages.dequeue();
	}
	
	/**
	 * Checks if there are no messages in the message queue.
	 * 
	 * @return is the message queue empty?
	 */
	protected boolean noMessages()
	{
		return messages.isEmpty();
	}
	
	/**
	 * Waits for a message to be sent to the message queue. This should
	 * only be called if the message queue is empty.
	 */
	protected void waitForMessage()
	{
		synchronized(messages) {
			try
			{
				messages.wait();
			}
			catch(InterruptedException e)
			{}
		}
	}
	
	/**
	 * This is where all the work of the message handler is done. A message
	 * is passed in and the something should be done to handle it obviously.
	 * 
	 * @param msg - the current message from the queue
	 */
	protected abstract void handleMessage(Message msg);
}
